package com.oromwin.repos.server.request.file;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.commons.logging.Log;

import com.oromwin.repos.common.Common;
import com.oromwin.repos.common.Logger;
import com.oromwin.repos.common.ReposException;
import com.oromwin.repos.common.Server;
import com.oromwin.repos.common.Common.Category;
import com.oromwin.repos.common.helper.DBHelper;
import com.oromwin.repos.common.helper.FSHelper;
import com.oromwin.repos.common.helper.IOHelper;
import com.oromwin.repos.data.AbstractReposTable;
import com.oromwin.repos.data.FileTable;
import com.oromwin.repos.data.MediaTable;
import com.oromwin.repos.server.LockObject;
import com.oromwin.repos.server.request.AbstractRequest;
import com.oromwin.repos.server.response.CreateResponse;
import com.oromwin.repos.server.response.Response;

public class MoveFileRequest extends AbstractRequest {
    private transient Log log = Logger.getLog(MoveFileRequest.class);
    // 클라이언트 정보
    private String SrcFileId = null;
    private String DstVolumeId = null;
    // 원본-서버 정보
    private FileTable SrcFileTable = null;
    private File SrcMediaPath = null;
    private MediaTable SrcMediaTable = null;
    // 대상-서버정보
    private MediaTable DstMediaTable = null;
    private File DstMediaPath = null;
    private String DstFileId = null;
    private FileTable DstFileTable = null;

    @Override
    public void receive(DataInputStream dis, LockObject lockObject) throws ReposException {
        try {
            synchronized (lockObject) {
                SrcFileId = lockObject.getFileId();
                DstVolumeId = IOHelper.readUTF8(dis);
                DstFileId = Server.UniqueId.generate();
            }
        } catch (IOException e) {
            String message = String.format("Failed to read stream, {%s}", e.getMessage());
            throw ReposException.create(log, e, message);
        } catch (Exception e) {
            String message = String.format("Unexpected exception, {%s}", e.getMessage());
            throw ReposException.create(log, e, message);
        }
    }

    public Response run() throws ReposException {
        CreateResponse res = null;
        try {
            getCurrentReposTables();
            checkFreeSpaceExists();
            checkDependentObjectNotExists();
            checkSameObjectExists();
            // 새로운 미디어 파일 생성
            String VolumeId = DstMediaTable.getVolumeId();
            String MediaId = DstMediaTable.getMediaId();
            String FileName = SrcFileTable.getFileName();
            String FileFormat = SrcFileTable.getFileFormat();
            File FilePath = Common.FilePath.generate(DstMediaPath, DstFileId);
            String FileCategory = Category.NORMAL;
            Boolean FileStatus = Boolean.FALSE;
            Long FileBytes = SrcFileTable.getFileBytes();
            Boolean FilePdfConvFlag = false;
            Boolean FileCompressionFlag = false;
            DstFileTable = new FileTable(VolumeId, MediaId, DstFileId, FileName, FileFormat, FilePath, FileBytes, FileCategory, FileStatus, FilePdfConvFlag, FileCompressionFlag, Common.datetime(), Common.datetime());
            FSHelper.cp(SrcFileTable.getFullFilepath(), DstFileTable.getFullFilepath());
            DBHelper.insertTable(DstFileTable);
            // 원본파일 및 메타정보를 삭제 (Copy와 차이점)
            String whereClause = String.format("FileId = '%s'", SrcFileId);
            FSHelper.rm(SrcFileTable.getFullFilepath());
            DBHelper.deleteTable(FileTable.class, whereClause);
            // 원본 미디어 정보를 업데이트
            whereClause = String.format("VolumeId = '%s' AND MediaId = '%s'", SrcFileTable.getVolumeId(), SrcFileTable.getMediaId());
            List<AbstractReposTable> srcFileTables = DBHelper.selectTable(FileTable.class, whereClause);
            int mediaFileCount = 0;
            long mediaUsedBytes = 0L;
            for (AbstractReposTable table : srcFileTables) {
                if (table instanceof FileTable) {
                    FileTable fileTable = (FileTable) table;
                    mediaFileCount++;
                    mediaUsedBytes += fileTable.getFileBytes();
                }
            }
            SrcMediaTable.setMediaUsedBytes(mediaUsedBytes);
            SrcMediaTable.setMediaFileCount(mediaFileCount);
            DBHelper.updateTable(SrcMediaTable, whereClause);
            // 대상 미디어 정보를 업데이트
            whereClause = String.format("VolumeId = '%s' AND MediaId = '%s'", DstFileTable.getVolumeId(), DstFileTable.getMediaId());
            List<AbstractReposTable> dstFileTables = DBHelper.selectTable(FileTable.class, whereClause);
            mediaFileCount = 0;
            mediaUsedBytes = 0L;
            for (AbstractReposTable table : dstFileTables) {
                if (table instanceof FileTable) {
                    FileTable fileTable = (FileTable) table;
                    mediaFileCount++;
                    mediaUsedBytes += fileTable.getFileBytes();
                }
            }
            DstMediaTable.setMediaUsedBytes(mediaUsedBytes);
            DstMediaTable.setMediaFileCount(mediaFileCount);
            DBHelper.updateTable(DstMediaTable, whereClause);
            res = new CreateResponse(true, "Succeeded to move file", DstFileId);
        } catch (ReposException e) {
            res = new CreateResponse(false, e.getMessage(), id());
        } catch (Exception e) {
            String message = String.format("Unexpected exception, {%s}", e.getMessage());
            throw ReposException.create(log, e, message);
        }
        return res;
    }

    @Override
    public void checkFreeSpaceExists() throws ReposException {
        // 1. 디스크 여유공간이 충분한지 확인
        long freespace = DstMediaTable.getMediaFreeBytes();
        freespace += DstMediaTable.getMediaFreeBytes();
        long requested = SrcFileTable.getFileBytes();
        if (freespace < requested) {
            String message = String.format("Requested file size is too big, freespace:requested {%d:%d}", freespace, requested);
            throw ReposException.create(log, message);
        }
    }

    @Override
    public void checkDependentObjectNotExists() throws ReposException {
     // 1. 복사할 원본파일이 존재하는지 확인
        File file = SrcFileTable.getFullFilepath();
        if (!FSHelper.ls(file)) {
            String message = String.format("Source filename does not exist on the server, path {%s}", file.toString());
            throw ReposException.create(log, message);
        }
    }

    @Override
    public void checkSameObjectExists() throws ReposException {
        // 1. 복사할 대상 폴더에 동일한 파일이 있는지 확인
        File FilePath = Common.FilePath.generate(DstMediaPath, DstFileId);
        File file = new File(FilePath, DstFileId);
        if (FSHelper.ls(file)) {
            String message = String.format("Same Destination filename already exist on the server, path {%s}", file.toString());
            throw ReposException.create(log, message);
        }
    }

    @Override
    public void getCurrentReposTables() throws ReposException {
        // 1. 원본 파일정보를 데이터베이스에서 가져옴
        AbstractReposTable reposTable = null;
        String whereClause = String.format("FileStatus = 0 AND FileId = '%s'", SrcFileId);
        List<AbstractReposTable> reposTables = DBHelper.selectTable(FileTable.class, whereClause);
        if (reposTables != null && reposTables.size() == 1) {
            reposTable = reposTables.get(0);
        } else {
            String message = String.format("'%s' FileId does not exists in database, tableSize:whereClause {%d:%s}", SrcFileId, reposTables.size(), whereClause);
            throw ReposException.create(log, message); 
        }
        if (reposTable instanceof FileTable) {
            SrcFileTable = (FileTable) reposTable;
        } else {
            String message = String.format("Illegal file table type, class {%s}", reposTable.getClass().toString());
            throw ReposException.create(log, message);
        }
        SrcMediaPath = SrcFileTable.getFilePath();
        // 1. 원본파일의 미디어 정보를 데이터베이스에서 가져옴
        AbstractReposTable srcReposTable = null;
        whereClause = String.format("MediaId = '%s' AND MediaStatus = 0", SrcFileTable.getMediaId());
        List<AbstractReposTable> srcReposTables = DBHelper.selectTable(MediaTable.class, whereClause);
        if (srcReposTables != null && srcReposTables.size() == 1) {
            srcReposTable = srcReposTables.get(0);
        } else {
            String message = String.format("'%s' MediaPath does not exists in database, whereClause {%s}", SrcMediaPath, whereClause);
            throw ReposException.create(log, message); 
        }
        if (srcReposTable instanceof MediaTable) {
            SrcMediaTable = (MediaTable) srcReposTable;
        } else {
            String message = String.format("Illegal media table type, class {%s}", srcReposTable.getClass().toString());
            throw ReposException.create(log, message);
        }
        // 1. 대상 미디어 정보를 데이터베이스에서 가져옴 - 여러개가 있다면 여유공간이 많은 미디어를 선택
        whereClause = String.format("VolumeId = '%s' AND MediaStatus = 0 ", DstVolumeId);
        List<AbstractReposTable> dstReposTables = DBHelper.selectTable(MediaTable.class, whereClause);
        if (dstReposTables != null && dstReposTables.size() == 1) {
            AbstractReposTable TempTable = dstReposTables.get(0);
            if (TempTable instanceof MediaTable) {
                DstMediaTable = (MediaTable) TempTable;
            } else {
                String message = String.format("Illegal media table type, class {%s}", TempTable.getClass().toString());
                throw ReposException.create(log, message);
            }
        } else if (dstReposTables.size() > 0) {
            // 여러개가 있다면 용량이 큰 미디어를 선택
            long freeBytes = 0;
            for (AbstractReposTable TempTable : dstReposTables) {
                if (TempTable instanceof MediaTable) {
                    MediaTable TempMediaTable = (MediaTable) TempTable;
                    long mediaBytes = TempMediaTable.getMediaFreeBytes(); 
                    if (freeBytes < mediaBytes) {
                        freeBytes = mediaBytes;
                        DstMediaTable = TempMediaTable;
                    }
                } else {
                    String message = String.format("Illegal media table type, class {%s}", TempTable.getClass().toString());
                    throw ReposException.create(log, message);
                }
            }
        } else {
            String message = String.format("'%s' MediaId does not exists in database, whereClause {%s}", DstVolumeId, whereClause);
            throw ReposException.create(log, message); 
        }
        DstMediaPath = DstMediaTable.getMediaPath();
    }
}
